Skip to content

Refresh the video calling screens with the design system tokens - #1827

Open
andremion wants to merge 10 commits into
develop-v2from
andrerego/and-1425-refresh-the-video-calling-screen-with-the-design-system
Open

Refresh the video calling screens with the design system tokens#1827
andremion wants to merge 10 commits into
develop-v2from
andrerego/and-1425-refresh-the-video-calling-screen-with-the-design-system

Conversation

@andremion

@andremion andremion commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Goal

Closes AND-1425

The in-call screens move to the design system tokens and the base components from AND-1420. This is a refresh, not a redesign, so the layout and the functionality stay as they are on develop-v2. The scope rules are on the parent issue AND-1421.

Implementation

SDK:

  • CallAppBar uses StreamIconButton for the back button, a stroke token for the recording dot and a typography token for the center text.
  • Participant tiles, labels, indicators and reactions use spacing, radius and emoji tokens. FloatingParticipantVideo uses the size and radius tokens and keeps its drag behavior.
  • Large avatar initials scale with the avatar instead of staying at one small size, capped at the size the 1.x SDK used.
  • The call action buttons default to the 48dp Large size. The base component rebuild had left them at 40dp, the 1.x SDK drew 44dp and Figma asks for 48dp.
  • The hifi audio toggle goes back to the music note and music off icons. The design icon set has no music glyph and the sliders icon reads as a settings control.
  • The ringing screens and the audio call screen paint their background with the app color instead of the surface color. The design system maps the secondary button background to that same surface color, so the mic, camera and speaker buttons had no visible shape.

Demo app:

  • The call screen and the share panel use tokens and the design icons. The share panel keeps the surface color because it is a card, so its Call ID button uses the outlined secondary style.
  • The call activity draws edge to edge, as MainActivity does since AND-1513.
  • The settings button received the negated menu state, so it looked pressed while the menu was closed.

🎨 UI Changes

1.x on the left, this branch on the right.

Lobby

lobby

In call

call

In call, dark mode

call_dark

Camera off, participant initials

call_initials

Camera off, participant initials, dark mode

call_initials_dark

Share panel

share

Share panel, dark mode

share_dark

Testing

Ran on two emulators, a Pixel 4 on API 30 with the 1.x demo app and a Pixel 8 on API 35 with this branch, in a shared call. Checked the lobby, the call grid, the camera-off tiles, the muted state, the share panel and the ringing screens, in light and dark mode.

To reproduce: start a test call on one device, join it from a second one, then open the share panel and toggle the camera and the microphone on both.

Validation on the final tree:

./gradlew spotlessCheck apiCheck testDebugUnitTest
./gradlew :stream-video-android-ui-compose:recordPaparazziDebug
./gradlew :stream-video-android-ui-compose:verifyPaparazziDebug

All green. The snapshots were re-recorded once, deliberately, as the parent issue allows. There is no detekt task in this repo.

Summary by CodeRabbit

  • New Features

    • Added music and music-off icons for HiFi audio controls.
    • Large avatars now scale initials text appropriately for improved readability.
  • UI Improvements

    • Call controls now use larger default button sizing.
    • Updated call screens, invite dialogs, sharing layouts, and participant actions with consistent spacing, colors, typography, icons, and button styles.
    • Improved call backgrounds, recording indicators, video cards, and spotlight corner styling.
    • Enabled edge-to-edge display in the demo call experience.
    • Updated the default back button and call action visuals for consistency.

The call app bar back button becomes a StreamIconButton, and its recording
dot border and center text use the stroke token and the body text style.
The remaining literal paddings and radii on the participant label, the
generic indicator and the spotlight containers move to tokens. The
reaction emoji animates to the emoji size token. The floating self view
takes the 144x208 size tokens on device and in the preview, with one
radius for its shape and clip.

Avatars of 80dp and larger rendered their initials with the 14sp badge
style, because no token text style exists for that size. Their initials
now scale to 40 percent of the avatar, so the 100dp tile fallback and the
200dp ringing avatars read at a glance.

Goldens change for the floating self view size and for the initials of
the 80dp and 100dp avatars.
Content paddings, the app bar padding and spacer, the floating self view
clip, the participant actions padding and the bad network label move to
StreamTokens and the theme typography.

The share panel gets the same treatment: its paddings and radii move to
tokens, the add-user and copy icons become their design system
equivalents, and the QR caption is explicitly white because the QR box is
black in both themes. The popup close button becomes a StreamIconButton
with the design system X, in the text color instead of white.

Layout and behavior are unchanged.
The SDK call activity content already pads for the system bars, but only
MainActivity opted into edge to edge. Below API 35 the call screen kept
the opaque status and navigation bar bands.
The toggle received the negated menu state, so the active style applied
while the menu was closed. On the old theme the active and inactive styles
had the same dark background, which hid the mistake. The design system
active style is the brand color, so the button looked pressed all the time.
The design system rebuild set the call actions to the 40dp Medium size.
The 1.x SDK drew them at 44dp, and the Figma call controls and app bar
buttons are 48dp, which is the existing Large size. Every call action and
the app bar back button now default to Large. Callers that pass a size
keep it.
The design system rebuild put the sliders icon on both states of the
toggle, which reads as a settings control. The icon set has no music
glyph, so the toggle goes back to the material music note and music off
icons the 1.x SDK used, vendored as vector drawables in ui-core like the
other material icons there.
The design system maps the secondary button background to the
surface-default color. The ringing screens and the audio call screen
painted their full-screen background with that same color, so the mic,
camera and speaker buttons had no visible shape. Those screens now use
the app background, like every other full screen in the demo app.

The share panel keeps the surface color because it is a card, so its
Call ID button uses the outlined secondary style instead.
@andremion andremion added the pr:improvement Enhances an existing feature or code label Sep 11, 2026
@andremion

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@github-actions

Copy link
Copy Markdown
Contributor

PR checklist ✅

All required conditions are satisfied:

  • Title length is OK (or ignored by label).
  • At least one pr: label exists.
  • Sections ### Goal, ### Implementation, and ### Testing are filled, or the PR is bot-authored.
  • An issue is linked (Linear ticket or GitHub issue), or the PR is bot-authored.

🎉 Great job! This PR is ready for review.

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions

Copy link
Copy Markdown
Contributor

SDK Size Comparison 📏

SDK Before After Difference Status
stream-video-android-core 12.30 MB 12.45 MB 0.14 MB 🟢
stream-video-android-ui-xml 5.70 MB 5.75 MB 0.05 MB 🟢
stream-video-android-ui-compose 6.23 MB 3.23 MB -3.00 MB 🚀

@coderabbitai

coderabbitai Bot commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 7d30e02b-7bfd-48a7-ac66-532067876fab

📥 Commits

Reviewing files that changed from the base of the PR and between 48a33eb and 9305195.

⛔ Files ignored due to path filters (62)
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_CompoundComponentFactoryTest_compound_component_factory_overrides_a_single_component.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_call_app_bar_with_default_title.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_call_lobby_controls.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_control_actions_with_default_handler.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_incoming_call_content.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_incoming_call_content_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_outgoing_call_content.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_outgoing_call_content_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_participant_video_fallback.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.theme_VideoComponentFactoryTest_factory_screen_sharing_fallback.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_accept_call_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_chat_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_closed_captions_toggle_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_decline_call_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_flip_camera_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_leave_call_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_screen_share_toggle_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_settings_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_toggle_camera_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_toggle_hifi_audio_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_toggle_microphone_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.controls.actions_ControlActionsTest_toggle_speakerphone_action.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_LandscapeVideoRendererTest_landscape_participants_2.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_LandscapeVideoRendererTest_landscape_participants_2_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_LandscapeVideoRendererTest_landscape_participants_3.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_LandscapeVideoRendererTest_landscape_participants_3_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_PortraitVideoRendererTest_portrait_participants_2.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_PortraitVideoRendererTest_portrait_participants_2_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_PortraitVideoRendererTest_portrait_participants_3.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose.ui.components.call.renderer.internal_PortraitVideoRendererTest_portrait_participants_3_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_AudioCallContentTest_audio_call_content.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_AudioCallContentTest_audio_call_content_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_AudioCallContentTest_audio_only_call_content.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_AudioCallContentTest_audio_only_call_content_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_AudioRoomTest_audio_room.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_AudioRoomTest_audio_room_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallComponentsPortraitTest_call_app_bar.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_call_content_deprecated_overload.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_call_content_deprecated_overload_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_call_content_with_multiple_participants.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_call_content_with_multiple_participants_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_incoming_call_content_with_minimum_parameters.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_incoming_call_content_with_minimum_parameters_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_incoming_call_content_with_multiple_participants.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_incoming_call_content_with_multiple_participants_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_incoming_call_content_with_one_participant.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_incoming_call_content_with_one_participant_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_outgoing_call_content_with_minimum_parameters_and_video_type.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_outgoing_call_content_with_minimum_parameters_and_video_type_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_outgoing_call_content_with_multiple_participants.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_outgoing_call_content_with_multiple_participants_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_outgoing_call_content_with_one_participant.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallContentTest_outgoing_call_content_with_one_participant_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallControlsTest_call_controls.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallLobbyTest_call_lobby.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallLobbyTest_call_lobby_deprecated_overload.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_CallLobbyTest_call_lobby_with_camera_disabled.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_ParticipantsPortraitTest_call_participants_info_options.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_ParticipantsPortraitTest_call_participants_list.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_ParticipantsPortraitTest_call_participants_list_in_dark_mode.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_ParticipantsPortraitTest_local_video_content.png is excluded by !**/*.png
  • stream-video-android-ui-compose/src/test/snapshots/images/io.getstream.video.android.compose_ParticipantsPortraitTest_local_video_content_in_dark_mode.png is excluded by !**/*.png
📒 Files selected for processing (30)
  • demo-app/src/main/kotlin/io/getstream/video/android/CallActivity.kt
  • demo-app/src/main/kotlin/io/getstream/video/android/ui/call/CallScreen.kt
  • demo-app/src/main/kotlin/io/getstream/video/android/ui/call/DemoComponentFactory.kt
  • demo-app/src/main/kotlin/io/getstream/video/android/ui/call/ShareCall.kt
  • stream-video-android-ui-compose/api/stream-video-android-ui-compose.api
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/avatar/InitialsAvatar.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/background/CallBackground.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/base/StreamButtonSize.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/CallAppBar.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/activecall/AudioCallContent.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/AcceptCallAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/CancelCallAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ChatDialogAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ClosedCaptionsToggleAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/DeclineCallAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/FlipCameraAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/GenericActions.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/LeaveCallAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ScreenShareToggleAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/SettingsAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ToggleCameraAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ToggleHifiAudioAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ToggleMicrophoneAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/controls/actions/ToggleSpeakerphoneAction.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/FloatingParticipantVideo.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/ParticipantVideo.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/call/renderer/internal/Common.kt
  • stream-video-android-ui-compose/src/main/kotlin/io/getstream/video/android/compose/ui/components/indicator/GenericIndicator.kt
  • stream-video-android-ui-core/src/main/res/drawable/stream_video_ic_music_note.xml
  • stream-video-android-ui-core/src/main/res/drawable/stream_video_ic_music_off.xml
💤 Files with no reviewable changes (1)
  • stream-video-android-ui-compose/api/stream-video-android-ui-compose.api

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


Walkthrough

The pull request aligns call UI components with Stream design tokens, shared controls, resource-backed icons, larger default buttons, updated backgrounds, proportional avatar text, and revised participant rendering behavior.

Changes

Call UI design-system alignment

Layer / File(s) Summary
Call controls and shared resources
stream-video-android-ui-compose/api/..., stream-video-android-ui-compose/src/main/kotlin/.../call/..., stream-video-android-ui-core/src/main/res/drawable/*
Call actions now default to large buttons. The call app bar uses shared controls and tokens. Music-note resources support HiFi audio states.
Demo call surfaces
demo-app/src/main/kotlin/.../CallActivity.kt, demo-app/src/main/kotlin/.../ui/call/*
The demo app enables edge-to-edge rendering and replaces call-screen literals and Material controls with shared tokens, styles, and resources.
Rendering and theme updates
stream-video-android-ui-compose/src/main/kotlin/.../avatar/*, .../background/*, .../renderer/*, .../indicator/*
Avatar text scales with large avatar size. Call backgrounds use the app background color. Video rendering uses shared dimensions, radii, spacing, and reaction sizing.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Refactor

Suggested reviewers: pratimmallick

Merge Risk: ⚪ Minimal · up to 93051

The call UI refresh preserves the reviewed control wiring and visual behavior, with validation completed across call states and themes.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 19.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 36 functions across 27 files. (2 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly summarizes the primary change: updating video calling screens to use design system tokens.
Description check ✅ Passed The description covers the goal, implementation, UI changes with screenshots, testing steps, validation commands, and linked issues. The contributor checklist, reviewer checklist, and GIF section are …
Full details: Docstring Coverage

Explanation

Docstring coverage is 19.44% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 36 functions across 27 files. (2 skipped: 2 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 2
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch andrerego/and-1425-refresh-the-video-calling-screen-with-the-design-system

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

A rabbit hops through tokens bright
Buttons grow large in moonlit light
Music notes guide each toggle’s tune
Avatars bloom beneath the moon
Shared styles tidy every call
Soft paws celebrate them all

Comment @coderabbitai help to get the list of available commands.

@andremion
andremion marked this pull request as ready for review September 11, 2026 10:32
@andremion
andremion requested a review from a team as a code owner September 11, 2026 10:32
val scaled = with(LocalDensity.current) {
(this@toAvatarTextStyle * LARGE_AVATAR_TEXT_RATIO).toSp()
}
val fontSize = if (scaled > LARGE_AVATAR_MAX_TEXT_SIZE) LARGE_AVATAR_MAX_TEXT_SIZE else scaled

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking, but the ceiling here isn't absolute.

Dp.toSp() is (value / fontScale).sp, so scaled has already had the user's font scale divided out. Comparing that against a fixed 48.sp makes the effective ceiling move with the font-scale setting instead of pinning it at 48dp:

  • font scale 1.0 → scaled = 80sp → capped → initials render at 48dp
  • font scale 2.0 → scaled = 40sp → under the cap → initials render at ~80dp

On the 200dp avatar in ParticipantAvatars.kt that's a visibly different result from what LARGE_AVATAR_MAX_TEXT_SIZE's KDoc promises. Clamping in Dp before the conversion keeps it absolute:

val capped = minOf(this@toAvatarTextStyle * LARGE_AVATAR_TEXT_RATIO, LARGE_AVATAR_MAX_TEXT_SIZE)
val fontSize = with(LocalDensity.current) { capped.toSp() }
typography.headingLarge.copy(fontSize = fontSize, lineHeight = fontSize)

with LARGE_AVATAR_MAX_TEXT_SIZE becoming 48.dp. At font scale 1 the output is identical, so the re-recorded snapshots stay valid — I verified verifyPaparazziDebug passes either way.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, you're right. Fixed in 072ab8e, clamping in Dp before the conversion like you suggested. Goldens verify without re-recording, so font scale 1 is unchanged.

@@ -78,6 +81,18 @@ internal fun Dp.toAvatarTextStyle(): TextStyle {
this < StreamTokens.size32 -> typography.captionEmphasis
this < StreamTokens.size48 -> typography.bodyEmphasis
this < StreamTokens.size80 -> typography.headingLarge

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not for this PR — a parity note, since Chat and Video are now on the same design tokens.

Chat's equivalent ladder (AvatarPlaceholder.kt, Dp.toPlaceholderTextStyle()) picks a different style in two of the bands both SDKs share:

avatar size Chat here
32–39dp captionEmphasis bodyEmphasis
48–79dp headingMedium headingLarge

Chat also keys off named AvatarSize constants rather than raw size tokens, which gives consumers a size vocabulary to match against.

Related: the reason the else branch has to compute anything is that numericExtraLarge is fontSizeSm (14sp) despite its name — and it's the same in Chat. If that token were fixed in both SDKs this could go back to being a plain lookup, which is the pattern Chat follows throughout.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Took the band alignment in 40af253, video follows chat now below 80dp. Four goldens move and it's 20sp to 18sp on the initials, under one percent of the pixels.

One thing on numericExtraLarge though: I checked the design source and numeric.xl maps to font-size.sm there, so the 14sp is intentional. That scale is micro, xxs, xs, sm for badge counts, so extra large means the biggest badge rather than a big heading. Both SDKs read it correctly. The else branch still has to compute because our avatars go up to 200dp and chat tops out at 80dp.

I'll flag the ladder with Jurgen so he can correct both SDKs if chat is the one that's off.

.background(color = VideoTheme.colors.backgroundCoreApp),
) {
Column(
modifier = modifier

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-existing, not introduced here — flagging it only because it's adjacent to the line you changed.

The inner Column re-applies modifier, which the outer Box two lines up already has. A caller passing AudioOnlyCallContent(modifier = Modifier.padding(24.dp)) gets the padding twice, and a caller-supplied .background(...) would repaint over the backgroundCoreApp this PR just introduced. Should be Modifier. Same pattern at lines 113/119.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 7aa1e86, both sites. Pre-existing as you say, but the background change makes the repaint case real, so it seemed worth taking here.

Dp.toSp divides the font scale out, so comparing the converted value
against a fixed 48.sp let the ceiling grow with the user's font size
setting. On the 200dp ringing avatar the initials rendered at about
80dp at font scale 2, instead of the 48dp the constant promises.

The clamp now happens in Dp, before the conversion. The output at font
scale 1 is unchanged, so the snapshots stay as recorded.
The box and the column inside it both took the caller's modifier, so a
caller passing padding got it twice, and a caller passing a background
painted over the screen background. The details slot had the same
problem. The inner layouts now start from Modifier.

Found by the review on the call screen refresh. It predates that work,
but the background change makes the repaint case visible.
@andremion
andremion enabled auto-merge (squash) September 11, 2026 14:28
Chat picks a different text style in two of the bands the two SDKs
share: caption for 32 to 39dp and a medium heading for 48 to 79dp.
Nothing asked for the difference, so video follows chat now. The
computed branch above 80dp stays, because chat has no avatar that large.

Four goldens move, all in the 48 to 79dp band: the 72dp preview, the
48dp invite row and the audio room grid. The initials go from 20sp to
18sp, which is under one percent of the pixels in each image.
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

pr:improvement Enhances an existing feature or code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants